import * as vscode from 'vscode';
import { MagicApiClient } from './magicApiClient';
import { ServerManager } from './serverManager';

function globToRegExp(glob: string): RegExp {
    // Very simple glob -> regex: supports ** and * only
    const escaped = glob
        .replace(/[.+^${}()|\[\]\\]/g, '\\$&')
        .replace(/\*\*/g, '.*')
        .replace(/\*/g, '[^/]*');
    return new RegExp('^' + escaped + '$');
}

function pathIncluded(path: string, includes?: readonly string[]): boolean {
    if (!includes || includes.length === 0) return true;
    return includes.some(gl => globToRegExp(gl).test(path));
}

function pathExcluded(path: string, excludes?: readonly string[]): boolean {
    if (!excludes || excludes.length === 0) return false;
    return excludes.some(gl => globToRegExp(gl).test(path));
}

function getExtForType(type: string): string {
    return type === 'datasource' ? '.json' : '.ms';
}

export class MagicFileSearchProvider implements vscode.FileSearchProvider {
    public client: MagicApiClient | null;
    constructor(client?: MagicApiClient | null) {
        this.client = client || ServerManager.getInstance().getCurrentClient();
    }

    async provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Promise<vscode.Uri[]> {
        const client = this.client || ServerManager.getInstance().getCurrentClient();
        if (!client) return [];
        const pattern = (query.pattern || '').trim();
        const uris: vscode.Uri[] = [];
        try {
            await client.getResourceTree(false);
            const dirs = await client.getResourceDirs();
            for (const dir of dirs) {
                if (token.isCancellationRequested) break;
                const files = await client.getResourceFiles(dir);
                for (const f of files) {
                    if (token.isCancellationRequested) break;
                    const ext = getExtForType(String(f.type));
                    const fileKey = `${f.groupPath}/${f.name}${ext}`;
                    // includes/excludes
                    if (!pathIncluded(fileKey, options.includes)) continue;
                    if (pathExcluded(fileKey, options.excludes)) continue;
                    // name pattern simple match
                    if (pattern && !fileKey.includes(pattern) && !f.name.includes(pattern)) continue;
                    uris.push(vscode.Uri.parse(`magic-api:/${fileKey}`));
                    if (options.maxResults && uris.length >= options.maxResults) {
                        return uris;
                    }
                }
            }
        } catch (e) {
            console.warn('[Magic API] 文件搜索失败:', e);
        }
        return uris;
    }
}

export class MagicTextSearchProvider implements vscode.TextSearchProvider {
    public client: MagicApiClient | null;
    constructor(client?: MagicApiClient | null) {
        this.client = client || ServerManager.getInstance().getCurrentClient();
    }

    private buildMatcher(query: vscode.TextSearchQuery): { find(text: string): { index: number; length: number } | null } {
        const pattern = query.pattern || '';
        if (!pattern) {
            return { find: () => null };
        }
        if (query.isRegExp) {
            const flags = `${query.isCaseSensitive ? '' : 'i'}`;
            const re = new RegExp(pattern, flags);
            return {
                find(text: string) {
                    const m = re.exec(text);
                    if (!m) return null;
                    const idx = m.index;
                    const len = m[0].length;
                    return { index: idx, length: len };
                }
            };
        }
        const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
        const word = query.isWordMatch ? `\\b${escaped}\\b` : escaped;
        const flags = `${query.isCaseSensitive ? '' : 'i'}`;
        const re = new RegExp(word, flags);
        return {
            find(text: string) {
                const m = re.exec(text);
                if (!m) return null;
                const idx = m.index;
                const len = m[0].length;
                return { index: idx, length: len };
            }
        };
    }

    async provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress<vscode.TextSearchResult>, token: vscode.CancellationToken): Promise<vscode.TextSearchComplete> {
        const client = this.client || ServerManager.getInstance().getCurrentClient();
        if (!client) return { limitHit: false };
        const keyword = (query.pattern || '').trim();
        if (!keyword) return { limitHit: false };
        try {
            await client.getResourceTree(false);
            const raw = await client.searchWorkbench(keyword);
            const matcher = this.buildMatcher(query);
            let count = 0;
            const max = options.maxResults ?? Infinity;
            for (const item of raw) {
                if (token.isCancellationRequested) break;
                const id: string = String(item.id || '');
                const lineNum = Number(item.line || 0);
                const text: string = String(item.text || '');
                const fileKey = client.getPathById(id);
                if (!fileKey) continue;
                if (!pathIncluded(fileKey, options.includes)) continue;
                if (pathExcluded(fileKey, options.excludes)) continue;

                const match = matcher.find(text);
                if (!match) continue;

                // Build preview respecting charsPerLine
                const charsPerLine = options.previewOptions?.charsPerLine ?? 200;
                const startSlice = Math.max(0, match.index - Math.floor(charsPerLine / 2));
                const endSlice = Math.min(text.length, match.index + match.length + Math.floor(charsPerLine / 2));
                const previewText = text.slice(startSlice, endSlice);
                const previewRange = new vscode.Range(0, match.index - startSlice, 0, match.index - startSlice + match.length);

                const result: vscode.TextSearchResult = {
                    uri: vscode.Uri.parse(`magic-api:/${fileKey}`),
                    ranges: new vscode.Range(new vscode.Position(lineNum, match.index), new vscode.Position(lineNum, match.index + match.length)),
                    preview: {
                        text: previewText,
                        matches: previewRange
                    }
                };
                progress.report(result);
                count++;
                if (count >= max) break;
            }
            return { limitHit: count >= (options.maxResults ?? Infinity) };
        } catch (e) {
            console.warn('[Magic API] 文本搜索失败:', e);
            return { limitHit: false, message: (e as any)?.message } as any;
        }
    }
}